package com.shaw.study.springx.context;

import com.shaw.study.springx.annotation.Autowired;
import com.shaw.study.springx.annotation.Controller;
import com.shaw.study.springx.annotation.Service;
import com.shaw.study.springx.aop.AopProxy;
import com.shaw.study.springx.beans.AopConfigDefinition;
import com.shaw.study.springx.beans.BeanDefinition;
import com.shaw.study.springx.beans.BeanPostProcessor;
import com.shaw.study.springx.beans.BeanWapper;
import com.shaw.study.springx.context.support.ApplicationContextAware;
import com.shaw.study.springx.context.support.BeanDefinitionReader;
import com.shaw.study.springx.context.support.DefaultListableBeanFactory;
import com.shaw.study.springx.core.BeanFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * Created by asus on 2018/4/19.
 */
public class ClassPathXmlApplicationContext extends DefaultListableBeanFactory implements BeanFactory,ApplicationContextAware {


    public ClassPathXmlApplicationContext(String[] configPaths){
        refresh(configPaths);
    }

    public ClassPathXmlApplicationContext(String configPath){
        refresh(new String[]{configPath});
    }

    protected void refresh(String[] configPath){
        //创建BeanDefinitionReader,进行配置文件的定位
        beanDefinitionReader = new BeanDefinitionReader(configPath);
        //解析
        classList = beanDefinitionReader.praseProperties();
        //注册所有的元素
        registryBeanDeifinition();
        //注册aopbean
        initAopBeanDefinition();
        //初始化
        doAutowired();
    }

    /**
     * 初始化所有的Bean
     */
    private void doAutowired() {
        if(beanDefinitionMap.isEmpty()){
            return;
        }
       for(Map.Entry<String,BeanDefinition> entry:beanDefinitionMap.entrySet()){
            BeanDefinition beanDefinition = entry.getValue();
            //判断是否是懒加载
            if(beanDefinition.isLazyInit()){
                continue;
            }
            //进行实例化，并对属性进行赋值
           try {
               getBean(beanDefinition.getFactoryBeanName());
           } catch (Exception e) {
               e.printStackTrace();
           }
       }
    }

    /**
     * 注册所有Bean
     */
    private void registryBeanDeifinition() {
        for(String classPath:classList){
            try {
                Class clazz = Class.forName(classPath);
                if(clazz.isInterface()||
                        (!clazz.isAnnotationPresent(Controller.class)
                                &&!clazz.isAnnotationPresent(Service.class))){
                    continue;
                }
                BeanDefinition beanDefinition = beanDefinitionReader.doRegisterBeanDefinitions(clazz);
                beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
                //当前类的所有父接口都需要注册为当前beanDefinition,为了在autowired接口能够找到beanDefintion进行初始自动注入
                Class<?>[] interfaces = clazz.getInterfaces();
                for (Class<?> c : interfaces) {
                    beanDefinitionMap.put(c.getName(), beanDefinition);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * 获取bean的实例
     * @param beanName
     * @return
     * @throws Exception
     */
    @Override
    public Object getBean(String beanName) throws Exception {
        BeanWapper beanWapper = beanWapperMap.get(beanName);
        //如果beanWapper已经封装则直接返回
        if(beanWapper!=null){
            return beanWapperMap.get(beanName).getWapperBeanInstance();
        }
        BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        if(beanDefinition==null){
            throw new Exception("no this bean");
        }
        //创建bean的实例
        Object beanInstance = createBeanInstance(beanDefinition);
        //生成通知事件
        BeanPostProcessor beanPostProcessor = new BeanPostProcessor();

        //bean的包装类实例化之前通知一次
        beanPostProcessor.postProcessBeforeInitialization(beanInstance,beanName);

        //进行属性的注入
        populateBean(beanInstance);

        //bean的包装器
        beanWapper = new BeanWapper(beanInstance,new AopProxy(aopConfigDefinitionList),beanPostProcessor);
        //将包装后的实例放入wapperMap中
        beanWapperMap.put(beanName,beanWapper);

        //bean的包装类实例化之后通知一次
        beanPostProcessor.postProcessAfterInitialization(beanInstance,beanName);

        return beanWapperMap.get(beanName).getWapperBeanInstance();
    }

    //创建bean实例
    private Object createBeanInstance(BeanDefinition beanDefinition) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Object beanInstance;
        String className = beanDefinition.getBeanClassName();
        if(!beanCacheMap.containsKey(className)){
            //如果实例没有创建，则进行实例的创建
            Class<?> clazz = Class.forName(className);
            beanInstance = clazz.newInstance();
            //存储bean实例缓存中
            beanCacheMap.put(className,beanInstance);
        }
        if(!beanDefinition.isSingleton()){
            //如果不是bean不是单例配置，则进行克隆并返回
        }

        beanInstance = beanCacheMap.get(className);
        return beanInstance;
    }

    /**
     *  bean实例的属性值的注入（@Autowired标签注释的属性）
     * @param beanInstance
     */
    private void populateBean(Object beanInstance) {
        Class clazz = beanInstance.getClass();
        //如果不是Controller或者service则不进行注入直接返回
        if (!(clazz.isAnnotationPresent(Controller.class)
                || clazz.isAnnotationPresent(Service.class))) {
            return;
        }
        Field[] fields = clazz.getDeclaredFields();
        for(Field field:fields){
            Autowired autowired = field.getAnnotation(Autowired.class);
            if(autowired==null){
                continue;
            }
            String autowiredBeanName = autowired.value();
            if("".equals(autowiredBeanName)){
                if(field.getType().isInterface()) {
                    autowiredBeanName = field.getType().getName();
                }else{
                    autowiredBeanName = beanDefinitionReader.getFirstLowerCase(field.getType().getSimpleName());
                }
            }
            field.setAccessible(true);
            try {
                //如果beanWapperMap获取WapperBean为空则去创建次Bean
                Object wapperBeanInstance =  null;
                if(beanWapperMap.get(autowiredBeanName)==null||beanWapperMap.get(autowiredBeanName).getWapperBeanInstance()==null){
                    //如果实例为空则创建一个实例
                    wapperBeanInstance = getBean(autowiredBeanName);
                }
                wapperBeanInstance = beanWapperMap.get(autowiredBeanName).getWapperBeanInstance();
                field.set(beanInstance,wapperBeanInstance);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 获取配置文件
     * @return
     */
    public Properties getConfig(){
        return this.beanDefinitionReader.getConfig();
    }

    /**
     * 获取所有的beanname
     * @return
     */
    public String[] getBeanDefinitionNames(){
        return  beanDefinitionMap.keySet().toArray(new String[beanDefinitionMap.size()]);
    }

    public static void main(String[] args) {
        String scanPackage = "com.shaw.study.test";
//        char[] chars = s.toCharArray();
//        for(int i = 0;i<chars.length;i++){
//            if(chars[i]=='.'){
//                chars[i] = '/';
//            }
//        }
        URL url = ClassPathXmlApplicationContext.class.getResource("/");
        System.out.println(scanPackage);
    }

    @Override
    public void setApplicationContext(ClassPathXmlApplicationContext applicationContext) {

    }

    @Override
    protected void initAopBeanDefinition(){
        Properties config = beanDefinitionReader.getConfig();
        try {
            if(config==null){
                return;
            }
            //由于配置文件设计成只配置一个aop所以此处就不使用循环的方式了
            String pointcut = config.getProperty("ponitcut");
            String beforeMethodStr = config.getProperty("aspectBefore");
            String afterMethodStr = config.getProperty("aspectAfter");

            String classPath = beforeMethodStr.substring(0, beforeMethodStr.lastIndexOf('.'));

            Class<?> aspectClass = Class.forName(classPath);

            Pattern pattern = pointcut != null ? Pattern.compile(pointcut): null ;
            Method beforeMethod = null;
            Method afterMethod = null;

            for (Method method : aspectClass.getMethods()) {
                if (method.toString().indexOf(beforeMethodStr) != -1) {
                    beforeMethod = method;
                    continue;
                }
                if (method.toString().indexOf(afterMethodStr) != -1) {
                    afterMethod = method;
                    continue;
                }
            }
            AopConfigDefinition aopConfigDefinition = new AopConfigDefinition(pattern, beforeMethod, afterMethod);

            this.aopConfigDefinitionList.add(aopConfigDefinition);
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}
